-
Notifications
You must be signed in to change notification settings - Fork 7.9k
PDO_Firebird: Supported Firebird 4.0 datatypes #14897
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This patch requires Firebird 4.0 header files to be present in the build system, since type identifiers SQL_INT128, SQL_DEC16, SQL_DEC34, SQL_TIMESTAMP_TZ, SQL_TIME_TZ are required. |
Thank you, I will check this as soon as possible. |
For Windows, the |
fbclient is exactly compatible. Even fbclient version 5.0 works fine with Firebird 2.5. As for the header files, I don’t see any problems here either. My patch just uses a few constants declared as #define. That is, functions specific to the new fbclient are not used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes look good to me, but need to get the version of firebird in CI on windows to 4 (I created a PR)
#14912
I don't understand why the LINUX_X64_RELEASE_NTS test is failing... (It should be using 4.02)
ext/pdo_firebird/firebird_driver.c
Outdated
static void set_coercing_data_type(XSQLDA* sqlda) | ||
{ | ||
/* Data types introduced in Firebird 4.0 are difficult to process using the Firebird Legacy API. */ | ||
/* These data types include DECFLOAT(16), DECFLOAT(34), INT128 (NUMERIC/DECIMAL(38, x)), */ | ||
/* TIMESTAMP WITH TIME ZONE, and TIME WITH TIME ZONE. In any case, at least the first three data types */ | ||
/* can only be mapped to strings. The last two too, but for them it is potentially possible to set */ | ||
/* the display format, as is done for TIMESTAMP. This function allows you to ensure minimal performance */ | ||
/* of queries if they contain columns and parameters of the above types. */ | ||
unsigned int i; | ||
short dtype; | ||
short nullable; | ||
XSQLVAR* var; | ||
for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { | ||
dtype = (var->sqltype & ~1); /* drop flag bit */ | ||
nullable = (var->sqltype & 1); | ||
switch(dtype) { | ||
case SQL_INT128: | ||
var->sqltype = SQL_VARYING + nullable; | ||
var->sqllen = 46; | ||
var->sqlscale = 0; | ||
break; | ||
case SQL_DEC16: | ||
var->sqltype = SQL_VARYING + nullable; | ||
var->sqllen = 24; | ||
break; | ||
case SQL_DEC34: | ||
var->sqltype = SQL_VARYING + nullable; | ||
var->sqllen = 43; | ||
break; | ||
case SQL_TIMESTAMP_TZ: | ||
var->sqltype = SQL_VARYING + nullable; | ||
var->sqllen = 58; | ||
break; | ||
case SQL_TIME_TZ: | ||
var->sqltype = SQL_VARYING + nullable; | ||
var->sqllen = 46; | ||
break; | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please standardize indentation to TAB.
static void set_coercing_data_type(XSQLDA* sqlda) | |
{ | |
/* Data types introduced in Firebird 4.0 are difficult to process using the Firebird Legacy API. */ | |
/* These data types include DECFLOAT(16), DECFLOAT(34), INT128 (NUMERIC/DECIMAL(38, x)), */ | |
/* TIMESTAMP WITH TIME ZONE, and TIME WITH TIME ZONE. In any case, at least the first three data types */ | |
/* can only be mapped to strings. The last two too, but for them it is potentially possible to set */ | |
/* the display format, as is done for TIMESTAMP. This function allows you to ensure minimal performance */ | |
/* of queries if they contain columns and parameters of the above types. */ | |
unsigned int i; | |
short dtype; | |
short nullable; | |
XSQLVAR* var; | |
for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { | |
dtype = (var->sqltype & ~1); /* drop flag bit */ | |
nullable = (var->sqltype & 1); | |
switch(dtype) { | |
case SQL_INT128: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 46; | |
var->sqlscale = 0; | |
break; | |
case SQL_DEC16: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 24; | |
break; | |
case SQL_DEC34: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 43; | |
break; | |
case SQL_TIMESTAMP_TZ: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 58; | |
break; | |
case SQL_TIME_TZ: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 46; | |
break; | |
} | |
} | |
} | |
static void set_coercing_data_type(XSQLDA* sqlda) | |
{ | |
/* Data types introduced in Firebird 4.0 are difficult to process using the Firebird Legacy API. */ | |
/* These data types include DECFLOAT(16), DECFLOAT(34), INT128 (NUMERIC/DECIMAL(38, x)), */ | |
/* TIMESTAMP WITH TIME ZONE, and TIME WITH TIME ZONE. In any case, at least the first three data types */ | |
/* can only be mapped to strings. The last two too, but for them it is potentially possible to set */ | |
/* the display format, as is done for TIMESTAMP. This function allows you to ensure minimal performance */ | |
/* of queries if they contain columns and parameters of the above types. */ | |
unsigned int i; | |
short dtype; | |
short nullable; | |
XSQLVAR* var; | |
for (i=0, var = sqlda->sqlvar; i < sqlda->sqld; i++, var++) { | |
dtype = (var->sqltype & ~1); /* drop flag bit */ | |
nullable = (var->sqltype & 1); | |
switch(dtype) { | |
case SQL_INT128: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 46; | |
var->sqlscale = 0; | |
break; | |
case SQL_DEC16: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 24; | |
break; | |
case SQL_DEC34: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 43; | |
break; | |
case SQL_TIMESTAMP_TZ: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 58; | |
break; | |
case SQL_TIME_TZ: | |
var->sqltype = SQL_VARYING + nullable; | |
var->sqllen = 46; | |
break; | |
} | |
} | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, there is an EMPTY_SWITCH_DEFAULT_CASE()
macro that can be used for the default case where the switch does nothing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you insert this macro, the driver crashes as soon as the dtype goes beyond the cases described in case. In my case, for other types you just need to do nothing.
ext/pdo_firebird/firebird_driver.c
Outdated
} | ||
} | ||
} | ||
/* }}} */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably not necessary /* }}} */
ext/pdo_firebird/firebird_driver.c
Outdated
#if FB_API_VER >= 40 | ||
/* set coercing a data type */ | ||
set_coercing_data_type(&S->out_sqlda); | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto, please use consistent indentation.
ext/pdo_firebird/firebird_driver.c
Outdated
#if FB_API_VER >= 40 | ||
/* set coercing a data type */ | ||
set_coercing_data_type(S->in_sqlda); | ||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
--EXTENSIONS-- | ||
pdo_firebird | ||
--SKIPIF-- | ||
<?php require('skipif.inc'); ?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Versions lower than 4 may want to skip this test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How to set this restriction?
$dbh->getAttribute(PDO::ATTR_SERVER_VERSION);
Gets the server version, but it looks like this
Firebird/Linux/AMD/Intel/x64 (access method), version "LI-V5.0.1.1391 Firebird 5.0 faca113"
Firebird/Linux/AMD/Intel/x64 (remote server), version "LI-V5.0.1.1391 Firebird 5.0 faca113/tcp (DESKTOP-E3INAFT)/P18:C"
Firebird/Linux/AMD/Intel/x64 (remote interface), version "LI-V5.0.1.1391 Firebird 5.0 faca113/tcp (DESKTOP-E3INAFT)/P18:C"
on disk structure version 13.1
You also need to use a regular expression to make this information simple.
On the other hand, the engine version can be obtained by request
select rdb$get_context('SYSTEM', 'ENGINE_VERSION')
from rdb$database
There is only a version number. But in any case, the server version can only be found after connecting to the database.
Create a separate function that returns the engine version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What matters here is the client version, not the server version. So, could you please try the following constant?
PDO::ATTR_CLIENT_VERSION
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It also returns a complex version of the client
LI-V6.3.1.1391 Firebird 5.0
The success of this test depends less on the client (although it does too) and more on the Firebird header files.
#if FB_API_VER >= 40
...
#endif
FB_API_VER is defined in ibase.h
, and if the header file is from Firebird 3.0, this branch simply will not end up in the binary file when compiled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you assuming the case that there is a difference between the client version and FB_API_VER
? That's certainly a possible problem.
Now, probably the simplest solution here is to be able to retrieve FB_API_VER
. In fact, this is useful for users when debugging on their own.
Could you please wait a moment while I open the PR to add?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, sure. The API version is essentially the version of the header files. And the client version is the version of fbclient.dll
or libfbclient.so
. They are generally independent.
What is the point of the patch? After preparing query, the server says I have these types of columns and input parameters. We cannot process some of them, so in response we tell the server, instead of these types, return VARCHAR(N) to us. And this part, in theory, can generally work independently of the client (I have not tested).
Now, probably the simplest solution here is to be able to retrieve FB_API_VER. In fact, this is useful for users when debugging on their own.
Could you please wait a moment while I open the PR to add?
OK. I also think this will be useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened PR
#14916
--EXTENSIONS-- | ||
pdo_firebird | ||
--SKIPIF-- | ||
<?php require('skipif.inc'); ?> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
@sim1984 edit:
|
I added API version checking. But I don’t like that the API version can be checked only after connecting to the database. |
I have no idea how to skip the test before connecting to the database. If you just start connect it in "--SKIPIF--" section. But now at least it’s clear why the test fails on Linux x64. The header files there are not from Firebird 4.0. |
Maybe add a driver-specific function |
Indeed, that doesn't make much sense, since it is not related to the Firebird server at all. Not sure if there is precedent in PDO, but can't we just have a constant which contains the API version (something like PS: or maybe instead of a constant a static method on |
PDO constants have a very strong impression of being attribute keys, and since the API version is int and the value is small, if implement it as a constant, it may have the same value as the PDO core attribute key. Therefore, there is a concern that bugs may occur due to user misunderstandings. Therefore, if want to make this more convenient, I think it makes the most sense to implement it as a static method. I think it's very meaningful to be able to have this kind of discussion before release :) |
done |
Right. Since the one from Ubuntu is installed, it's probably |
The change to replace attribute value retrieval with static method retrieval has been merged into master :) |
done |
Thanks, I will test this later on my local and confirm! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sim1984
Sorry for the late confirmation.
It looks mostly good, but the format of the date in the test expected value is different in my environment, causing the test to fail.
There doesn't seem to be a way to easily specify this, so could you please fix the expected value for a test that doesn't care about the date format?
--EXPECT-- | ||
{ | ||
"I64": 15, | ||
"I128": "15", | ||
"N": "123.97", | ||
"N2": "123.97", | ||
"TS": "2024-05-04 12:59:34", | ||
"TS_TZ": "2024-05-04 12:59:34.2390 Europe\/Moscow", | ||
"T": "12:59:34", | ||
"T_TZ": "12:59:34.2390 Europe\/Moscow", | ||
"DF16": "1.128", | ||
"DF34": "1.128" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my environment, the date format is probably different than yours.
005- "TS_TZ": "2024-05-04 12:59:34.2390 Europe\/Moscow",
006+ "TS_TZ": "04-MAY-2024 12:59:34.2390 Europe\/Moscow",
I thought about how to specify the format, but I don't want to do that yet because I think it would be quite complicated.
Therefore, could you please change your test expectations to:
--EXPECT-- | |
{ | |
"I64": 15, | |
"I128": "15", | |
"N": "123.97", | |
"N2": "123.97", | |
"TS": "2024-05-04 12:59:34", | |
"TS_TZ": "2024-05-04 12:59:34.2390 Europe\/Moscow", | |
"T": "12:59:34", | |
"T_TZ": "12:59:34.2390 Europe\/Moscow", | |
"DF16": "1.128", | |
"DF34": "1.128" | |
} | |
--EXPECTF-- | |
{ | |
"I64": 15, | |
"I128": "15", | |
"N": "123.97", | |
"N2": "123.97", | |
"TS": "2024-05-04 12:59:34", | |
"TS_TZ": "%s 12:59:34.2390 Europe\/Moscow", | |
"T": "12:59:34", | |
"T_TZ": "12:59:34.2390 Europe\/Moscow", | |
"DF16": "1.128", | |
"DF34": "1.128" | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
--EXPECT-- | ||
{ | ||
"I128": "12", | ||
"N1": "12.34", | ||
"N2": "12.34", | ||
"TS_TZ": "2024-05-04 12:59:34.2390 Europe\/Moscow", | ||
"T_TZ": "12:59:00.0000 Europe\/Moscow", | ||
"DF16": "12.34", | ||
"DF34": "12.34" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
APIs like Do you know anything about this? If I can't find any clues here, I'm thinking of asking upstream. |
APIs like isc_decode_XXX don't really exist for types with time zones. They are in the new object-oriented API. It is mainly for C++, but CLOOP files can be generated for C. But I wouldn’t want to drag out quite large implementations of the new C API for the sake of a small improvement. It may be possible to isolate only the necessary functionality from there. Need to try. |
Thanks, I don't intend to think about this on a grand scale either. At the very least, I think this PR can be merged as soon as the tests are fixed. This means that the decoding issue can be addressed in a follow-up PR. (Of course, there are cases where we can choose not to do that) |
There is currently no way to set the output format for time zone types, so the %s substitution is used in the expected output.
As a follow-up to the commit which introduced support for Firebird 4.0+ data types[1], we add support for formats for types with time zones. Since this uses the newer Firebird C++ API, pdo_firebird now requires a C++ compiler to be built. [1] <#14897> Co-authored-by: Christoph M. Becker <cmbecker69@gmx.de> Closes GH-15230.
Implementation #14896